#include <QJsonObject>
#include <QJsonArray>

#include <osg/Geometry>
#include<GC_MakeSegment.hxx>

#include "LineStrip3D.h"
#include "JsonReader.h"
#include "GLSLPrograms.h"
#include "OcctOsg.h"
#include "OsgUtil.h"

// 类似于createEdgeProgram, 除了z值处理之外
osg::ref_ptr<osg::Program> createEdgeProgram2();

struct LineStrip3DPrivate
{
	LineStrip3DPrivate()
		: m_rep3D(new osg::Geometry())
	{
		m_rep3D->setNodeMask(D3LineMask | D3CurveMask);
		osg::ref_ptr<osg::Program> program = createEdgeProgram2();
		auto ss = m_rep3D->getOrCreateStateSet();
		ss->setAttributeAndModes(program, osg::StateAttribute::ON);
	}
	osg::ref_ptr<osg::Geometry> m_rep3D;
	std::vector <osg::Vec3d> m_points;
	osg::Vec4 m_color = { 1, 1, 1, 1 };
	bool m_selected = false;
};

LineStrip3D::LineStrip3D(INode* parent)
	: Ent3DCurve(parent)
	, d_ptr(new LineStrip3DPrivate)
{
}

void LineStrip3D::setPoints(const std::vector<osg::Vec3d> &pos)
{
	if (d_ptr->m_points != pos)
	{
		d_ptr->m_points = pos;
		addFlags(UPDATE_GEOMETRY);
	}
}

void LineStrip3D::setColor(const osg::Vec4 &color)
{
	if (d_ptr->m_color != color)
	{
		d_ptr->m_color = color;
		addFlags(UPDATE_GEOMETRY);
	}
}

const std::vector <osg::Vec3d>& LineStrip3D::getPoints() const
{
	return d_ptr->m_points;
}

const osg::Vec4& LineStrip3D::color() const
{
	return d_ptr->m_color;
}

void LineStrip3D::update3D()
{
	if (flags())
	{
		d_ptr->m_rep3D->getPrimitiveSetList().clear();
		bool draw = d_ptr->m_points.size() >= 2;
		if (d_ptr->m_points.size() == 2)
		{
			if (d_ptr->m_points[0] == d_ptr->m_points[1])
				draw = false;
		}
		if (draw)
		{
			auto va = new osg::Vec3Array();
			for (auto pt : d_ptr->m_points)
			{
				va->push_back(pt);
			}

			osg::ref_ptr<osg::Vec4Array> ca = new osg::Vec4Array();

			const osg::Vec4f& selectedColor = osg::Vec4f(1.f, 99.f / 255, 71.f / 255, 1.f);
			if (d_ptr->m_selected)
				ca->push_back(selectedColor);
			else
				ca->push_back(d_ptr->m_color);

			d_ptr->m_rep3D->setVertexArray(va);
			d_ptr->m_rep3D->setColorArray(ca.get(), osg::Array::BIND_OVERALL);
			d_ptr->m_rep3D->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINE_STRIP, 0, d_ptr->m_points.size()));
		}
		else
		{
			d_ptr->m_rep3D->setVertexArray(nullptr);
			d_ptr->m_rep3D->setColorArray(nullptr, osg::Array::BIND_OVERALL);
			d_ptr->m_rep3D->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES, 0, 0));
		}
		d_ptr->m_rep3D->dirtyGLObjects();
		removeFlags(0xFFFFFFFF);
	}
}

osg::Node * LineStrip3D::getRep3D() const
{
	return d_ptr->m_rep3D.get();
}

void LineStrip3D::setSelected(bool val)
{
	if (d_ptr->m_selected != val)
	{
		d_ptr->m_selected = val;
		addFlags(UPDATE_GEOMETRY);
	}
}

void LineStrip3D::save(QJsonObject & json) const
{
	Ent3DCurve::save(json);

	QJsonArray points;
	for (auto pt : d_ptr->m_points)
	{
		points.append(saveVec3f(pt));
	}
	json["points"] = points;
	json["color"] = saveVec4f(d_ptr->m_color);
}

void LineStrip3D::load(const QJsonObject& json, JsonReader& reader)
{
	Ent3DCurve::load(json, reader);

	QJsonArray pointsJson = json["points"].toArray();
	for (int i = 0; i < pointsJson.count(); i++)
	{
		auto ptJson = pointsJson.at(i);
		d_ptr->m_points.push_back(loadVec3f(ptJson.toArray()));
	}
	d_ptr->m_color = loadVec4f(json["color"].toArray());
}

std::vector<osg::Vec3d> LineStrip3D::handles() const
{
	if (d_ptr->m_points.size() == 2)
	{
		std::vector<osg::Vec3d> result;
		result.push_back(d_ptr->m_points[0]);
		result.push_back((d_ptr->m_points[0] + d_ptr->m_points[1]) * 0.5);
		result.push_back(d_ptr->m_points[1]);
		return result;
	}
	else
	{
		return d_ptr->m_points;
	}
}

void LineStrip3D::move(const osg::Vec3d& offset)
{
	for (int i = 0; i< d_ptr->m_points.size();i++)
	{
		d_ptr->m_points[i] = d_ptr->m_points[i] + offset;
	}
	addFlags(UPDATE_GEOMETRY);
}

void LineStrip3D::scale(const osg::Vec3d & center, double scale)
{
	for (auto& pt : d_ptr->m_points)
	{
		pt = center + (pt - center) * scale;
	}
}

void LineStrip3D::stretch(int index, const osg::Vec3d &offset)
{
	if (d_ptr->m_points.size() == 2)
	{
		switch (index)
		{
		case 0:
			d_ptr->m_points[0] = d_ptr->m_points[0] + offset;
			break;
		case 1:
			d_ptr->m_points[0] = d_ptr->m_points[0] + offset;
			d_ptr->m_points[1] = d_ptr->m_points[1] + offset;
			break;
		case 2:
			d_ptr->m_points[1] = d_ptr->m_points[1] + offset;
			break;
		}
	}
	else
		d_ptr->m_points[index] = d_ptr->m_points[index] + offset;
	addFlags(UPDATE_GEOMETRY);
}

std::shared_ptr<Ent> LineStrip3D::clone() const
{
	auto line = std::make_shared<LineStrip3D>(getParent());
	line->copy(*this);
	return line;
}

bool LineStrip3D::explode(std::vector<std::shared_ptr<Ent>>& result) const
{
	if (d_ptr->m_points.size() > 2)
	{
		for (int i = 1; i < d_ptr->m_points.size(); i++)
		{
			std::shared_ptr<LineStrip3D> line = std::make_shared<LineStrip3D>(getParent());
			std::vector <osg::Vec3d> points;
			points.push_back(d_ptr->m_points[i - 1]);
			points.push_back(d_ptr->m_points[i]);
			line->setPoints(points);
			line->setColor(d_ptr->m_color);
			result.push_back(line);
		}
		return true;
	}
	else
	{
		return false;
	}
}

void LineStrip3D::copy(const Ent& ent)
{
	auto line = dynamic_cast<const LineStrip3D*>(&ent);
	if (line)
	{
		d_ptr->m_color = line->color();
		d_ptr->m_points = line->getPoints();
		addFlags(UPDATE_GEOMETRY);
	}
}

std::vector<Handle(Geom_Curve)> LineStrip3D::getCurve3d() const
{
	std::vector<Handle(Geom_Curve)> result;
	if (d_ptr->m_points.size() >= 2)
	{
		for (int i = 1; i < d_ptr->m_points.size(); i++)
		{
			if (d_ptr->m_points[i - 1] == d_ptr->m_points[i])
				continue;
			GC_MakeSegment maker(toPnt(d_ptr->m_points[i - 1]), toPnt(d_ptr->m_points[i]));
			if (maker.IsDone())
				result.push_back(maker.Value());
		}
	}
	return result;
}

void LineStrip3D::rotate(const double angle, const osg::Vec2d & basePoint)
{
	for (int i = 0; i < d_ptr->m_points.size(); i++)
	{
		rotate2D(basePoint.x(), basePoint.y(), angle, d_ptr->m_points[i]);
	}
	addFlags(UPDATE_GEOMETRY);
}
